package ubadb.core.components.bufferManager.bufferPool.replacementStrategies.touchcount;

import java.util.Collection;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Vector;

import java.util.Iterator;

import ubadb.core.common.Page;
import ubadb.core.components.bufferManager.bufferPool.BufferFrame;
import ubadb.core.components.bufferManager.bufferPool.replacementStrategies.PageReplacementStrategy;
import ubadb.core.exceptions.BufferFrameException;
import ubadb.core.exceptions.PageReplacementStrategyException;

public class TouchCountReplacementStrategy implements PageReplacementStrategy {
	private LinkedList<TouchBufferFrame> cold;
	private LinkedList<TouchBufferFrame> hot;
	private int bufferSize;
	private static String NAME = "TouchCount";
	
	public BufferFrame findVictim(Collection<BufferFrame> bufferFrames) throws PageReplacementStrategyException {
		if (bufferSize == 0) {
			initialize(bufferFrames);
		}
		
		LookForHotFrames();
		BalanceColdAndHot();
		
		TouchBufferFrame victim = popFirstUnpinnedCold();
		/*System.out.println("Size of hot = "+hot.size());
		System.out.println("Size of cold = "+cold.size());
		System.out.println(victim.getPage().getPageId());*/
		return victim;
	}
	
	private TouchBufferFrame popFirstUnpinnedCold() throws PageReplacementStrategyException {
		Iterator<TouchBufferFrame> it = cold.iterator();
		
		while (it.hasNext())
		{
			TouchBufferFrame frame = it.next();
			if (frame.canBeReplaced())
			{
				it.remove();
				return frame;
			}
		}
		throw new PageReplacementStrategyException("There is no unpinned cold frame");
	}

	private void BalanceColdAndHot() throws PageReplacementStrategyException {
		while (!queuesAreBalanced())
			coolLastHotFrame();
	}

	private void LookForHotFrames() {
		Iterator<TouchBufferFrame> iterator = cold.iterator();
		Collection<TouchBufferFrame> coldsToRemove = new LinkedList<TouchBufferFrame>();
		
		while (iterator.hasNext()) {
			TouchBufferFrame frame = (TouchBufferFrame)iterator.next();
			if (frame.getCount()>2){
				frame.setCount(0);
				hot.addLast(frame);
				coldsToRemove.add(frame);
			}
		}
		
		cold.removeAll(coldsToRemove);
	}

	private void initialize(Collection<BufferFrame> bufferFrames) {
		bufferSize = bufferFrames.size();
		Iterator<BufferFrame> iterator = bufferFrames.iterator();
		/**
		 * El algoritmo de inicialización hace lo siguiente:
		 * Mando mitad a hot, y la otra a cold
		 */
		hot = new LinkedList<TouchBufferFrame>();
		cold = new LinkedList<TouchBufferFrame>();
		int count = 0;
		while (iterator.hasNext())
		{
			TouchBufferFrame frame = (TouchBufferFrame)iterator.next();
			
			if (count < (bufferSize/2) )
				hot.addFirst(frame);
			else
				cold.addFirst(frame);
			++count;
		}
	}
	
	private void coolLastHotFrame() throws PageReplacementStrategyException {
		if (hot.size()>0)
		{
			TouchBufferFrame frame = hot.removeFirst();
			frame.setCount(1);
			cold.addLast(frame);
		}
		else throw new PageReplacementStrategyException("There is no hot frame!");
	}

	private boolean queuesAreBalanced() {
		return Math.abs(hot.size() - cold.size()) <= 1;
	}

	public BufferFrame createNewFrame(Page page) {
		TouchBufferFrame newFrame = new TouchBufferFrame(page);
		
		if (bufferSize != 0) {
			cold.addLast(newFrame);
		}
		
		return newFrame;
	}
	
	public String toString() {
		return NAME;
	}
}
